
#include "AlignResult.h"
#include "compress_algo.h"

AlignResultWrite::AlignResultWrite(string chrName, unsigned int readLength, unsigned int refLength, 
			unsigned int winSize, unsigned int bucketSize, string fileName) : 
			AlignResultBase(chrName, readLength, refLength, winSize, bucketSize, 
			ceil(refLength/(double)winSize), fileName){

	memset(countMatrix, 0, sizeof(ubit64_t)*cmatrixSize);
	_qscoreVec = new vector<char>[_numBucket];
	_posVec = new vector<unsigned int>[_numBucket];	
	_hitVec = new vector<int>[_numBucket];
	_strandVec = new vector<char>[_numBucket];
	_offsetVec = new vector<uchar2>[_numBucket];
	_subVec = new vector<char2>[_numBucket];
	_writeOffset = 0;
	safe_fopen(&_alignFile, _fileName.c_str(), "wb");
	safe_fopen(&_metaFile, (string(_fileName + SUFFIX_META)).c_str(), "wb");

	cout << "_chrName = " << _chrName << endl;
	cout << "_readLength = " << _readLength << endl;
	cout << "_refLength = " << refLength << endl;
	cout << "_winSize = " << _winSize << endl;
	cout << "_bucketSize = " << _bucketSize << endl;
	cout << "_numBucket = " << _numBucket << endl;
	cout << "_fileName = " << _fileName << endl;

	/*write the meta-data into the head of the output file*/
	const size_t strSize = _chrName.size();
	cout << "Write, strSize = " << strSize << endl;
	safe_fwrite(&strSize, sizeof(size_t), 1, _metaFile);
	safe_fwrite(_chrName.c_str(), sizeof(char), _chrName.size(), _metaFile);
	safe_fwrite(&_readLength, sizeof(unsigned int), 1, _metaFile);
	safe_fwrite(&_refLength, sizeof(unsigned int), 1, _metaFile);
	safe_fwrite(&_winSize, sizeof(unsigned int), 1, _metaFile);
	safe_fwrite(&_bucketSize, sizeof(unsigned int), 1, _metaFile);
	safe_fwrite(&_numBucket, sizeof(unsigned int), 1, _metaFile);
}


AlignResultWrite::~AlignResultWrite() {
	delete[] _qscoreVec;
	delete[] _posVec;
	delete[] _hitVec;
	delete[] _strandVec;
	delete[] _offsetVec;
	delete[] _subVec;

	if(_alignFile) {
		safe_fclose(_alignFile);
	}

	if(_metaFile) {
		safe_fclose(_metaFile);
	}
}

void AlignResultWrite::insert(AlignResult* result) {
	cout << "start insert.........................." << endl;
	cout << "There are " << result->_numAlign << " lines in the buffer" << endl;

	assert(_chrName == result->_chrName);
	assert(_readLength == result->_readLength);	

	for(unsigned long id = 0; id < result->getNumAlign(); id++) {
		push_back(result, id);
		//cout << id << endl;
	}
	cout << result->getNumAlign() << " lines inserted successfully" << endl;
}

void AlignResultWrite::close() {
	PRINT_FUNC_NAME;

	//flush the in-memory data into the disk
	for(unsigned int bucketId = 0; bucketId < _numBucket; bucketId++) {
		dump(bucketId);
		resetBucket(bucketId);
	}

	//write the meta-inforamtion into the disk, the data is written into another file
	for(unsigned int i = 0; i < _numBucket; i++) {
		unsigned int size = _writePos[i].size();
		safe_fwrite(&size, sizeof(unsigned int), 1, _metaFile);
		safe_fwrite(&(_writePos[i].front()), sizeof(BucketPos), size, _metaFile);
	}	

	safe_fclose(_metaFile);
	safe_fclose(_alignFile);
	_alignFile = NULL;
	_metaFile = NULL;

	//printWritePos();
}

unsigned int AlignResultWrite::getBucketId(AlignResult* result, const unsigned long id) {
	//cout << "id = " << id << endl;
	//cout << "_pos[id] = " << result->_pos[id] << endl;
	//cout << "_winSize = " << _winSize << endl; 

	return result->_pos[id]/_winSize;
}

void AlignResultWrite::push_back(AlignResult* result, const unsigned long id) {

	const unsigned int bucketId = getBucketId(result, id);
	assert(bucketId < _numBucket);
	//cout << "id = " << id << endl;

	/*if the bucket is fulll, we first dump the data into the disk*/
	if(_posVec[bucketId].size() + 1 > _bucketSize) {
		//dump to the disk
		dump(bucketId);
		//reset the vectors
		resetBucket(bucketId);
		//cout << "the bucket is full, data dumping done." << endl;
	}

	/*if this bucket is not full, push this element*/
	char* qscore = result->get_qscore(id);
	for(unsigned int i = 0; i < _readLength; i++) {
		_qscoreVec[bucketId].push_back(qscore[i]);
	}
	_posVec[bucketId].push_back(result->_pos[id]);
	_hitVec[bucketId].push_back(result->_hit[id]);
	_strandVec[bucketId].push_back(result->_strand[id]);
	_offsetVec[bucketId].push_back(result->_offset[id]);
	_subVec[bucketId].push_back(result->_sub[id]);

}

void AlignResultWrite::dump(const unsigned int bucketId) {

	const unsigned int numAlignWrite = _posVec[bucketId].size();

	/*insert into the bucket linked list, offset + numAlign*/
	_writePos[bucketId].push_back(BucketPos(_writeOffset, numAlignWrite));

	/*write the data to the disk, and update _writeOffset*/
	safe_fwrite(&(_qscoreVec[bucketId].front()), sizeof(char), numAlignWrite*_readLength, _alignFile);
	_writeOffset += (sizeof(char)*numAlignWrite*_readLength);

	safe_fwrite(&(_posVec[bucketId].front()), sizeof(unsigned int), numAlignWrite, _alignFile);
	_writeOffset += sizeof(unsigned int)*numAlignWrite;

	safe_fwrite(&(_hitVec[bucketId].front()), sizeof(int), numAlignWrite, _alignFile);
	_writeOffset += sizeof(int)*numAlignWrite;


	//w/o compression
	//safe_fwrite(&(_strandVec[bucketId].front()), sizeof(char), numAlignWrite, _alignFile);
	//_writeOffset += sizeof(char)*numAlignWrite;
	//w/ compression	
	const int numWordAllocate = ceil(numAlignWrite/(double)(sizeof(unsigned int)*8.0));
	unsigned int* word = new unsigned int[numWordAllocate];
	unsigned int numWord = 0;
	bitEncode(word, &numWord, &(_strandVec[bucketId].front()), numAlignWrite);
	assert(numWord == numWordAllocate);
	safe_fwrite(word, sizeof(unsigned int), numWord, _alignFile);
	_writeOffset += sizeof(unsigned int)*numWord;
	delete[] word;

	safe_fwrite(&(_offsetVec[bucketId].front()), sizeof(uchar2), numAlignWrite, _alignFile);
	_writeOffset += sizeof(uchar2)*numAlignWrite;

	safe_fwrite(&(_subVec[bucketId].front()), sizeof(char2), numAlignWrite, _alignFile);
	_writeOffset += sizeof(char2)*numAlignWrite;
}

void AlignResultWrite::resetBucket(const unsigned int bucketId) {
	_qscoreVec[bucketId].clear();
	_posVec[bucketId].clear();
	_hitVec[bucketId].clear();
	_strandVec[bucketId].clear();
	_offsetVec[bucketId].clear();
	_subVec[bucketId].clear();
	//cout << "resetBucket" << endl;
}

void AlignResultWrite::updateCountMatrix(const AlignResult* result, const unsigned long id) {

}
